package xueqiu

import (
	"context"
	"fmt"
	"gitee.com/gcom/gbox/errors"
	"gitee.com/gcom/stockdock/comm"
	s "gitee.com/gcom/stockdock/core/support"
	"gitee.com/gcom/stockdock/crawler"
	"gitee.com/gcom/stockdock/spider/csrc"
	"gitee.com/gcom/stockdock/spider/xueqiu"
	"time"
)

// 更新雪球股票报价
func UpdateStockQuote(ctx s.TracerCtx) error {
	list, err := csrc.GetStockAll(ctx.DB)
	if err != nil {
		s.WriteErrEvent(ctx, "查询股票失败", err)
		return err
	}
	s.WriteEvent(ctx, fmt.Sprintf("共计股票%d支", len(list)))

	var count, nodata, total = 0, 0, len(list) + 4 // 加上4个指数报价
	var codeCh = make(chan string)
	var sem = make(chan int)
	var dateCheck = ctx.Req == nil // 对于Job触发任务，需要校验交易日，Http接口触发不用校验
	defer close(codeCh)
	var entities = make([]*xueqiu.StockQuoteEntity, 0, 200)
	cancelCtx, cancel := context.WithCancel(context.Background())
	defer cancel()
	onValue := func(req crawler.CrawlRequest, stock *xueqiu.StockQuote) {
		if stock.Volume == 0 {
			nodata += 1
			s.WriteEvent(ctx, fmt.Sprintf("[%d,%d/%d]获取股票(%s:%s)指标，股票停牌", count, nodata, total, stock.Code, stock.Name))
		} else {
			count += 1
			s.WriteEvent(ctx, fmt.Sprintf("[%d,%d/%d]已获取股票(%s:%s)报价", count, nodata, total, stock.Code, stock.Name))
			stock.Date = time.Time(stock.Timestamp).Truncate(24 * time.Hour)
			if dateCheck {
				dateCheck = false
				if stock.Date != time.Now().Truncate(24*time.Hour) {
					s.WriteEvent(ctx, "今日非交易日，忽略同花顺股票日报价数据抓取定时任务")
					cancel()
					return
				}
			}
			if stock.Name == "上证指数" {
				stock.Code = "699001"
			}
			entities = append(entities, &xueqiu.StockQuoteEntity{StockQuote: stock})
		}

		if len(entities) == 200 || (count+nodata == total && len(entities) > 0) {
			_, err := xueqiu.UpdateStockQuote(ctx.DB, entities)
			if err != nil {
				s.WriteEvent(ctx, fmt.Sprintf("写入数据库失败，%v", err))
				close(sem)
				return
			}
			s.WriteEvent(ctx, fmt.Sprintf("[%d,%d/%d]写入数据库%d条", count, nodata, total, len(entities)))
			entities = entities[:0]
		}
		sem <- 1 // produce one ticket
	}
	onError := func(req crawler.CrawlRequest, err error) {
		s.WriteErrEvent(ctx, "获取股票历史报价失败", err)
		close(sem)
	}

	go func() {
		xueqiu.GetStockQuote(ctx, codeCh, onValue, onError)
	}()

	// 获取指数报价
	for i, v := range []string{"SH000001", "SZ399001", "SZ399006", "SH000300"} {
		codeCh <- v

		select {
		case _, ok := <-sem: // consume 1 ticket
			if !ok {
				err := errors.New(fmt.Sprintf("异常退出，共完成写入股票%d支，无效数据%d支，全部%d支", count, nodata, total))
				s.WriteErrEvent(ctx, "异常退出", err)
				return err // task aborted
			}
		case <-cancelCtx.Done():
			s.WriteEvent(ctx, "任务已取消")
			return nil
		}

		if i == 3 {
			break
		}

		time.Sleep(500 * time.Millisecond)
	}

	// 获取股票报价
	for i, v := range list {
		codeCh <- comm.StockSymbol(v.Code)

		select {
		case _, ok := <-sem: // consume 1 ticket
			if !ok {
				err = errors.New(fmt.Sprintf("异常退出，共获取报价股票%d支", count))
				s.WriteErrEvent(ctx, "异常退出", err)
				return err // task aborted
			}
		}

		if i == total-1 {
			close(sem)
			s.WriteEvent(ctx, fmt.Sprintf("共完成写入股票%d支，无效数据%d支", count, nodata))
			break
		}

		time.Sleep(500 * time.Millisecond)
	}
	return nil
}
